<template>
  <canvas ref="bubble" :width="width" :height="height" :style="style"></canvas>
</template>

<script>
  export default {
    props: {
      y: {
        type: Number,
        default: 0
      }
    },
    data() {
      return {
        width: 50,
        height: 80
      };
    },
    computed: {
      distance() {
        return Math.max(0, Math.min(this.y * this.ratio, this.maxDistance));
      },
      style() {
        return `width:${this.width / this.ratio}px;height:${this.height /
          this.ratio}px`;
      }
    },
    created() {
      this.ratio = window.devicePixelRatio;
      this.width *= this.ratio;
      this.height *= this.ratio;
      this.initRadius = 18 * this.ratio;
      this.minHeadRadius = 12 * this.ratio;
      this.minTailRadius = 5 * this.ratio;
      this.initArrowRadius = 10 * this.ratio;
      this.minArrowRadius = 6 * this.ratio;
      this.arrowWidth = 3 * this.ratio;
      this.maxDistance = 40 * this.ratio;
      this.initCenterX = 25 * this.ratio;
      this.initCenterY = 25 * this.ratio;
      this.headCenter = {
        x: this.initCenterX,
        y: this.initCenterY
      };
    },
    mounted() {
      this._draw();
    },
    methods: {
      _draw() {
        const bubble = this.$refs.bubble;
        let ctx = bubble.getContext("2d");
        ctx.clearRect(0, 0, bubble.width, bubble.height);

        this._drawBubble(ctx);

        this._drawArrow(ctx);
      },
      _drawBubble(ctx) {
        ctx.save();
        ctx.beginPath();

        const rate = this.distance / this.maxDistance;
        const headRadius =
          this.initRadius - (this.initRadius - this.minHeadRadius) * rate;

        this.headCenter.y =
          this.initCenterY - (this.initRadius - this.minHeadRadius) * rate;

        // 画上半弧线
        ctx.arc(
          this.headCenter.x,
          this.headCenter.y,
          headRadius,
          0,
          Math.PI,
          true
        );

        // 画左侧贝塞尔
        const tailRadius =
          this.initRadius - (this.initRadius - this.minTailRadius) * rate;
        const tailCenter = {
          x: this.headCenter.x,
          y: this.headCenter.y + this.distance
        };

        const tailPointL = {
          x: tailCenter.x - tailRadius,
          y: tailCenter.y
        };
        const controlPointL = {
          x: tailPointL.x,
          y: tailPointL.y - this.distance / 2
        };

        ctx.quadraticCurveTo(
          controlPointL.x,
          controlPointL.y,
          tailPointL.x,
          tailPointL.y
        );

        // 画下半弧线
        ctx.arc(tailCenter.x, tailCenter.y, tailRadius, Math.PI, 0, true);

        // 画右侧贝塞尔
        const headPointR = {
          x: this.headCenter.x + headRadius,
          y: this.headCenter.y
        };
        const controlPointR = {
          x: tailCenter.x + tailRadius,
          y: headPointR.y + this.distance / 2
        };
        ctx.quadraticCurveTo(
          controlPointR.x,
          controlPointR.y,
          headPointR.x,
          headPointR.y
        );

        ctx.fillStyle = "rgb(170,170,170)";
        ctx.fill();
        ctx.strokeStyle = "rgb(153,153,153)";
        ctx.stroke();
        ctx.restore();
      },
      _drawArrow(ctx) {
        ctx.save();
        ctx.beginPath();

        const rate = this.distance / this.maxDistance;
        const arrowRadius =
          this.initArrowRadius -
          (this.initArrowRadius - this.minArrowRadius) * rate;

        // 画内圆
        ctx.arc(
          this.headCenter.x,
          this.headCenter.y,
          arrowRadius - (this.arrowWidth - rate),
          -Math.PI / 2,
          0,
          true
        );

        // 画外圆
        ctx.arc(
          this.headCenter.x,
          this.headCenter.y,
          arrowRadius,
          0,
          (Math.PI * 3) / 2,
          false
        );

        ctx.lineTo(
          this.headCenter.x,
          this.headCenter.y - arrowRadius - this.arrowWidth / 2 + rate
        );
        ctx.lineTo(
          this.headCenter.x + this.arrowWidth * 2 - rate * 2,
          this.headCenter.y - arrowRadius + this.arrowWidth / 2
        );

        ctx.lineTo(
          this.headCenter.x,
          this.headCenter.y - arrowRadius + (this.arrowWidth * 3) / 2 - rate
        );

        ctx.fillStyle = "rgb(255,255,255)";
        ctx.fill();
        ctx.strokeStyle = "rgb(170,170,170)";
        ctx.stroke();
        ctx.restore();
      }
    },
    watch: {
      y() {
        this._draw();
      }
    }
  };
</script>

<style lang="less" scoped>
</style>